package dian.qing.li.demo.security.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Getter
public class QueryBuilder<T> {
    private QueryWrapper<T> query;
    private Map<String, Object> data;
    private Class<T> entityClass;

    public QueryBuilder(T entity) {
        this.entityClass = (Class<T>) entity.getClass();
        this.data = ObjUtils.toMap(entity);
        this.query = new QueryWrapper<>();
        this.query.select(entityClass, a -> true);
    }

    public QueryBuilder(T entity, boolean condition) {
        this.entityClass = (Class<T>) entity.getClass();
        this.data = ObjUtils.toMap(entity);
        this.query = new QueryWrapper<>();
        if (condition) {
            this.query.setEntity(entity);
        }
        this.query.select(entityClass, a -> true);
    }

    public QueryWrapper<T> query() {
        return this.query;
    }

    private List<String> parseFields(boolean property, String... fields) {
        List<String> list = new ArrayList<>(16);
        for (String field : fields) {
            if (StringUtils.isNotBlank(field)) {
                list.addAll(Lists.newArrayList(field.split(",")));
            }
        }
        list = list.stream()
                .filter(a -> this.data.containsKey(CodeUtils.humpField(a)))
                .map(a -> {
                    if (property) {
                        return CodeUtils.humpField(a);
                    }
                    return CodeUtils.underlineField(a);
                })
                .distinct()
                .collect(Collectors.toList());
        return list;
    }

    private List<String> property(String... fields) {
        return parseFields(true, fields);
    }

    private List<String> columns(String... fields) {
        return parseFields(false, fields);
    }

    private void condition(String type, String... fields) {
        List<String> properties = this.property(fields);
        if (CollectionUtils.isNotEmpty(properties)) {
            for (String key : properties) {
                Object value = this.data.get(key);
                boolean condition = value != null;
                if (value instanceof String && StringUtils.isBlank((String) value)) {
                    condition = false;
                }
                switch (type) {
                    case "eq":
                        this.query.eq(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "ne":
                        this.query.ne(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "gt":
                        this.query.gt(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "ge":
                        this.query.ge(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "lt":
                        this.query.lt(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "le":
                        this.query.le(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "like":
                        this.query.like(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "likeLeft":
                        this.query.likeLeft(condition, CodeUtils.underlineField(key), value);
                        break;
                    case "likeRight":
                        this.query.likeRight(condition, CodeUtils.underlineField(key), value);
                        break;
                }
            }
        }
    }

    public QueryBuilder eq(String... fields) {
        this.condition("eq", fields);
        return this;
    }

    public QueryBuilder ne(String... fields) {
        this.condition("ne", fields);
        return this;
    }

    public QueryBuilder ge(String... fields) {
        this.condition("ge", fields);
        return this;
    }

    public QueryBuilder lt(String... fields) {
        this.condition("lt", fields);
        return this;
    }

    public QueryBuilder le(String... fields) {
        this.condition("le", fields);
        return this;
    }

    public QueryBuilder gt(String... fields) {
        this.condition("gt", fields);
        return this;
    }

    public QueryBuilder like(String... fields) {
        this.condition("like", fields);
        return this;
    }

    public QueryBuilder likeLeft(String... fields) {
        this.condition("likeLeft", fields);
        return this;
    }

    public QueryBuilder likeRight(String... fields) {
        this.condition("likeRight", fields);
        return this;
    }

    public QueryBuilder groupBy(String... fields) {
        List<String> columns = this.columns(fields);
        if (CollectionUtils.isNotEmpty(columns)) {
            this.query.groupBy(columns.toArray(new String[columns.size()]));
        }
        return this;
    }

    public QueryBuilder orderByAsc(String... fields) {
        List<String> columns = this.columns(fields);
        if (CollectionUtils.isNotEmpty(columns)) {
            this.query.orderByAsc(columns.toArray(new String[columns.size()]));
        }
        return this;
    }

    public QueryBuilder orderByDesc(String... fields) {
        List<String> columns = this.columns(fields);
        if (CollectionUtils.isNotEmpty(columns)) {
            this.query.orderByDesc(columns.toArray(new String[columns.size()]));
        }
        return this;
    }

    public QueryBuilder select(String... fields) {
        List<String> columns = this.columns(fields);
        if (CollectionUtils.isNotEmpty(columns)) {
            this.query.select(columns.toArray(new String[columns.size()]));
        }
        return this;
    }
}
